from __future__ import division

from gameset.phoenicia.base import *


class Pier(Building):
    name    =_ ('Pier')
    size    =_ (3)
    cost    =_ (50)
    color   =_ (Brown.dark)
    staff   =_ (10)
    terrain =_ (set([Sea]))   # FIXME: Put in neighbourhood restrictions
    stores  =_ (Good(5000))
    
    trade_policy = Attribute(writable = True)
    
    piers  = DictAttribute()
    ships  = ListAttribute()
    routes = SetAttribute()
    
    def setup(self):
        super(Pier, self).setup()
        
        self.update_policy()
    
    def update_policy(self):    
        self.trade_policy = Policy(owner = self)
        
        for type in self.supply + self.demand:
            entry = PolicyEntry(about = type, label = type.plural)
            
            if type in self.supply:
                entry.add(PolicyValue(label = 'Import', min = 0, max = 16000, step = 250, value = 16000))
                entry.add(PolicyValue(label = 'Export'))
            else:
                entry.add(PolicyValue(label = 'Import'))
                entry.add(PolicyValue(label = 'Export', min = 0, max = 16000, step = 250, value = 16000))
            
            self.trade_policy.add(entry)
        
        # FIXME: Carry over policy values
    
    @property
    def all_routes(self):
        return set(self.city.every(TradeRoute))
    
    @property
    def potential_routes(self):
        return self.all_routes - self.routes
    
    def allow_route(self, route):
        if route in self.routes:
            return
        
        self._routes.add(route)
        self.update_policy()
        self.logistics.update_building(self)
    
    def disallow_route(self, route):
        if route not in self.routes:
            return
        
        self._routes.discard(route)
        self.update_policy()
        self.logistics.update_building(self)
    
    @property
    def supply(self):
        return flattened([good.type for good in traderoute.imports]
                         for traderoute in self.routes
                         if traderoute.imports is not None)
    
    @property
    def demand(self):
        return flattened([good.type for good in traderoute.exports]
                         for traderoute in self.routes
                         if traderoute.exports is not None)
    
    def urgency_of(self, good):
        if good not in self.demand or self.storage.free_storage_for(good) == 0:
            return 0
        else:
            return good.price
    
    @property
    def transfer_rate(self):
        return int(10 * self.staffing_level)
    
    def import_policy(self, type):
        return self.trade_policy.entry(type).value('Import')
    
    def export_policy(self, type):
        return self.trade_policy.entry(type).value('Export')
    
    @property
    def random_import(self):
        imports = [type for type in self.supply
                   if self.import_policy(type).can_grow]
        
        if imports:
            return random.choice(imports)
        else:
            return None
        
    def construction(self):
        for tile in self.area.adjacent_with(lambda tile: self.city.map.at(tile).is_water):
            self._piers[tile] = None
    
    def notify_of_ship(self, ship):
        piers = []
        
        for pier in self.piers.keys():
            if self.piers[pier] is None:
                piers.append(pier)
        
        if not piers:
            return
        
        self._ships.append(ship)
        
        pier = random.choice(piers)
        ship.target = pier
        self._piers[pier] = ship
    
    def clean_piers(self):
        for ship in self.ships:
            if not ship.exists:
                self._ships.remove(ship)
        
        for pier in self.piers.keys():
            if self.piers[pier] is not None and not self.piers[pier].exists:
                self._piers[pier] = None
    
    def day(self, day, month, year):
        self.clean_piers()
        
        if self.calendar.is_start_of_year:
            for type in self.supply + self.demand:
                self.import_policy(type).current = 0
                self.export_policy(type).current = 0


class Traderoute(Building):
    name    =_ ('Traderoute')
    color   =_ (Blue.dark)
    terrain =_ (set([Sea]))
    
    city_name = Attribute(writable = True)
    imports   = Attribute(writable = True)
    exports   = Attribute(writable = True)
    start     = Attribute(writable = True)
    length    = Attribute(writable = True)
    months    = Attribute(writable = True)
    
    def day(self, day, month, year):
        if day != self.start or month not in self.months:
            return
        
        target = self.city.random(Pier, lambda pier: self in pier.routes)
        
        if target is None:
            return
        
        ship = self.spawn(TradeShip, pier = target, length = self.length,
                          imports = self.imports, exports = self.exports)
        
        target.notify_of_ship(ship)
   
    @property
    def spawn_point(self):
        return self.position


class TradeShip(Seeker):
    color    =_ (Brown.dark)
    is_ship  =_ (True)    
    capacity =_ (1000)
    
    length   = Attribute()
    imports  = Attribute()
    exports  = Attribute()
    
    home = Attribute()
    pier = Attribute(writable = True)
    left = Attribute(initial = 0, writable = True)
    
    def birth(self):
        self.left = self.calendar.days(self.length) // self.speed
        
        if self.imports is not None:
            for goods in self.imports:
                self.create(goods)
    
    def return_home(self):
        self.target = self.home
    
    def on_arrival(self):
        if isinstance(self.target, TradeRoute):
            self.destroy()
    
    def on_idle(self):
        self.left -= 1
        
        if self.left < 0:
            return
        
        if (self.left == 0 or self.target is None or 
              self.pier is None or not self.pier.exists):
            self.left = -1
            self.return_home()
            return
        
        if self.imports is not None:
            for goods in self.imports:
                self.sell(goods.type)
        
        if self.exports is not None:
            for goods in self.exports:
                self.buy(goods)
    
    def sell(self, type):
        if type not in self.pier.supply:
            return
        
        policy = self.pier.import_policy(type).number_below
        goods  = self.storage.all(type).max(policy)
        max    = min(int(self.city.money * 100) // goods.price, self.pier.transfer_rate)
        
        self.storage.transfer_to(self.pier, goods.max(max), self.charge)
    
    def buy(self, goods):
        if goods.type not in self.pier.demand:
            return
        
        policy = self.pier.export_policy(goods.type).number_below
        goods  = self.pier.storage.all(goods.type).max(policy).max(goods.quantity)
        max    = self.pier.transfer_rate
        
        self.storage.transfer_from(self.pier.storage, goods.max(max), self.credit)
    
    def charge(self, goods):
        self.pier.import_policy(goods.type).current += goods.quantity
        self.city.money -= goods.quantity * goods.price / 100
    
    def credit(self, goods):
        self.pier.export_policy(goods.type).current += goods.quantity
        self.city.money += goods.quantity * goods.price / 100
